/*----------------------------------------------------------------------------
 * Copyright (c) <2013-2017>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "lwip/opt.h"

#if BRIDGE_SUPPORT /* don't build if not configured for use in lwipopts.h */

#include "netif/bridge.h"
#include "lwip/pbuf.h"
#include "lwip/dhcp.h"
#include "netif/driverif.h"
#include "lwip/icmp.h"
#include "lwip/stats.h"
#include "netif/proxy_arp.h"
#include "los_swtmr.h"

#ifndef DEBUG
#define DEBUG 0
#endif

struct lwip_bridge g_lwip_bridge;
static struct network_db_entry port_db[MAX_DB_NUM];
static struct network_db_entry mac_db[MAX_DB_NUM];
extern struct network_db_entry arp_db[MAX_DB_NUM];

unsigned short ageing_swtmr_handle = 0xffff;
#define default_ageing_ticks    30000
unsigned int g_ageing_time = 300; //300 seconds
void bridge_ageing_time_set(unsigned int second)
{
    g_ageing_time = second;
}

static void db_init(void)
{
    (void)memset_s(port_db, sizeof(port_db), '\0', sizeof(port_db));
    (void)memset_s(mac_db, sizeof(mac_db), '\0', sizeof(mac_db));
    (void)memset_s(arp_db, sizeof(arp_db), '\0', sizeof(arp_db));
}

struct network_db_entry *db_get(struct network_db_entry *network_db)
{
    int i;

    for (i=0; i<MAX_DB_NUM; i++) {
        if (!network_db[i].used)
            break;
    }
    if (i == MAX_DB_NUM)
        return NULL;

    network_db[i].used = 1;
    return &network_db[i];
}

static void db_free(struct network_db_entry *entry)
{
    entry->used = 0;
}

void db_cleanup(struct network_db_entry *nethash[])
{
    int i;

    for(i=0; i < BRIDGE_HASH_SIZE; i++)
    {
        struct network_db_entry *f = NULL;
        f = nethash[i];
        while (f != NULL) {
            struct network_db_entry *g = NULL;

            g = f->next_hash;
            bridge_hash_unlink(f);

            db_free(f);
            f = g;
        }
    }
}

static int db_network_lookup_and_replace(struct lwip_bridge *priv,
    struct pbuf *p, unsigned char *networkAddr)
{
    struct network_db_entry *db = NULL;
    struct eth_hdr *ethhdr = NULL;

    ethhdr = (struct eth_hdr *)p->payload;
    db = priv->nethash[bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN)];
    while (db != NULL)
    {
        if(!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            // replace the destination mac address
            if (memcpy_s((char *)ethhdr->dest.addr, sizeof(ethhdr->dest.addr), db->macAddr, NETIF_MAX_HWADDR_LEN) != 0) {
              PRINT_ERR("db_network_lookup_and_replace:memcpy_s fail!\n");
              return 0;
            }
            atomic_inc(&db->use_count);  /*lint !e64*/
            db->ageing_timer = g_ageing_time;

            return 1;
        }

        db = db->next_hash;
    }

    return 0;
}

static void db_network_insert(struct lwip_bridge *priv,
        unsigned char *macAddr, unsigned char *networkAddr)
{
    struct network_db_entry *db = NULL;
    int hash;

    hash = bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN);
    db = priv->nethash[hash];
    while (db != NULL)
    {
        if(!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            (void)memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN);
            db->ageing_timer = g_ageing_time;
            return;
        }

        db = db->next_hash;
    }

    db = db_get(mac_db);

    if(db == NULL) {
        PRINT_ERR("run out  nat25  table!\n");
        return;
    }

    (void)memcpy_s(db->networkAddr, sizeof(db->networkAddr), networkAddr, MAX_NETWORK_ADDR_LEN);
    (void)memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN);
    (void)atomic_set(&db->use_count, 1); /*lint !e64*/
    db->ageing_timer = g_ageing_time;

    bridge_hash_link(priv->nethash, db, hash);
}

static struct netif* db_port_lookup(struct lwip_bridge *priv,  unsigned char *macAddr)
{
    struct network_db_entry *db = NULL;
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];

    (void)memset_s(networkAddr, sizeof(networkAddr), 0, MAX_NETWORK_ADDR_LEN);

    networkAddr[0] = BRIDGE_MAC;
    if (memcpy_s(networkAddr + 1, MAX_NETWORK_ADDR_LEN - 1, macAddr, ETHER_ADDR_LEN) != 0) {
        return NULL;
    }

    db = priv->porthash[bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN)];
    while (db != NULL)
    {
        if (!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            return db->port;
        }

        db = db->next_hash;
    }

    return NULL;
}

static void db_port_insert(struct lwip_bridge *priv, unsigned char *macAddr, struct netif *port)
{
    struct network_db_entry *db = NULL;
    int hash;
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];

    (void)memset_s(networkAddr, MAX_NETWORK_ADDR_LEN, 0, MAX_NETWORK_ADDR_LEN);

    networkAddr[0] = BRIDGE_MAC;
    if (memcpy_s(networkAddr + 1, MAX_NETWORK_ADDR_LEN - 1, macAddr, ETHER_ADDR_LEN) != 0) {
       return;
    }

    hash = bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN);
    db = priv->porthash[hash];
    while (db != NULL)
    {
        if(!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            (void)memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN);
            db->port = port;
            db->ageing_timer = g_ageing_time;
            return;
        }

        db = db->next_hash;
    }

    db = db_get(port_db);

    if(db == NULL) {
        PRINT_ERR("run out  nat25  table!\n");
        return;
    }

    (void)memcpy_s(db->networkAddr, sizeof(db->networkAddr), networkAddr, MAX_NETWORK_ADDR_LEN);
    if (memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN) != 0) {
      PRINT_ERR("db_port_insert:memcpy_s fail!\n");
      return;
    }
    (void)atomic_set(&db->use_count, 1); /*lint !e64*/
    db->port = port;
    db->ageing_timer = g_ageing_time;

    bridge_hash_link(priv->porthash, db, hash);
}


unsigned short checksum(unsigned short *buffer, int size)
{

    unsigned long cksum=0;

    while(size > 1)
    {
        cksum += *buffer++;
        size -= 2;
    }

    if(size)
    {
        cksum += *(unsigned char *)buffer;
    }

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);

    return (unsigned short)(~cksum);
}

static void hash_list_check(struct network_db_entry *hash[])
{
    struct network_db_entry *db = NULL;
    int i;

    for (i = 0; i < BRIDGE_HASH_SIZE; i++)
    {
        db = hash[i];
        while (db != NULL)
        {
            if (db->used == 1)
            {
                if (db->ageing_timer > 0)
                {
                    db->ageing_timer--;
                }
                else
                {
                    bridge_hash_unlink(db);
                    db_free(db);
                }
            }

            db = db->next_hash;
        }
    }
}

void bridge_ageing_check_func(unsigned int arg)
{
    struct lwip_bridge *bridge_handle = (struct lwip_bridge *)arg;

    hash_list_check(bridge_handle->nethash);
}

static int bridge_ageing_swtmr_create(struct lwip_bridge *arg)
{
    unsigned int ret;
    ret = LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, (SWTMR_PROC_FUNC)bridge_ageing_check_func,
        &ageing_swtmr_handle, (uintptr_t)arg);
    if(ret != LOS_OK){
        PRINT_ERR("LOS_SwtmrCreate error %d\n", ret);
        return -1;
    }

    ret = LOS_SwtmrStart(ageing_swtmr_handle);
    if(ret != LOS_OK){
        (void)LOS_SwtmrDelete(ageing_swtmr_handle);
        PRINT_ERR("LOS_SwtmrStart error %d\n", ret);
        return -1;
    }

    return 0;
}

static int bridge_ageing_swtmr_delete(void)
{
    unsigned int ret;

    ret = LOS_SwtmrDelete(ageing_swtmr_handle);
    if(ret != LOS_OK){
        PRINT_ERR("LOS_SwtmrStart error %d\n", ret);
        return -1;
    }

    return 0;
}

void bridge_init(void)
{
    int i;

    db_init();
    (void)memset_s(g_lwip_bridge.nethash, sizeof(g_lwip_bridge.nethash), 0, BRIDGE_HASH_SIZE);
    (void)memset_s(g_lwip_bridge.br_mac, sizeof(g_lwip_bridge.br_mac), 0, NETIF_MAX_HWADDR_LEN);
    (void)memset_s(g_lwip_bridge.br_ip, sizeof(g_lwip_bridge.br_ip), 0, sizeof(g_lwip_bridge.br_ip));
    for (i = 0; i < MAX_SRC_NETWORK_NUM; i++) {
        g_lwip_bridge.src[i] = NULL;
    }
    g_lwip_bridge.dst = NULL;
    g_lwip_bridge.br_enable = FALSE;
    g_lwip_bridge.init_flag = TRUE;
}

/* current only support one rule, direct repeater to root ap */
int bridge_set_forward_rule(char *src_ifn[], int src_count, char *dst_ifn) /*lint -e505*/
{
    int i;

    struct netif *src[MAX_SRC_NETWORK_NUM] = {NULL};
    struct netif *dst = NULL;

    if (g_lwip_bridge.init_flag != TRUE) {
        return ERR_ARG;
    }

    if (src_count > MAX_SRC_NETWORK_NUM) {
        return ERR_ARG;
    }

    for (i = 0; i < src_count; i++) {
        src[i] = netif_find(src_ifn[i]);
     if (NULL == src[i]) {
            return ERR_ARG;
     }
    }

    if (NULL == (dst = netif_find(dst_ifn))) {
      return ERR_ARG;
    }

    (void)memcpy_s(g_lwip_bridge.br_mac, sizeof(g_lwip_bridge.br_mac), dst->hwaddr, NETIF_MAX_HWADDR_LEN);
    g_lwip_bridge.dst = dst;
    (void)memcpy_s(g_lwip_bridge.src, sizeof(g_lwip_bridge.src), src, sizeof(src));
    if (memcpy_s(g_lwip_bridge.br_ip, sizeof(g_lwip_bridge.br_ip), &dst->ip_addr, sizeof(ip_addr_t)) != 0) {
      return ERR_VAL;
    }

    bridge_print("br_mac[%02x %02x %02x %02x %02x %02x], br_ip[%d %d %d %d], br_netif_name:%s\n",
        g_lwip_bridge.br_mac[0], g_lwip_bridge.br_mac[1], g_lwip_bridge.br_mac[2], g_lwip_bridge.br_mac[3],
        g_lwip_bridge.br_mac[4], g_lwip_bridge.br_mac[5], g_lwip_bridge.br_ip[0], g_lwip_bridge.br_ip[1],
        g_lwip_bridge.br_ip[2], g_lwip_bridge.br_ip[3], g_lwip_bridge.dst->name);

    return 0;
} /*lint +e505*/

void bridge_ip_forward(bool flag)
{
    unsigned int ret;

    if (g_lwip_bridge.init_flag != TRUE)
    {
        PRINT_ERR("Bridge has not been initialized!\n");
        return;
    }

    if (g_lwip_bridge.src[0] == NULL || g_lwip_bridge.dst == NULL)
    {
        PRINT_ERR("The forward rule of bridge has not been set!\n");
        return;
    }

    if (flag == TRUE)
    {
        if (g_lwip_bridge.br_enable == FALSE)
        {
            ret = bridge_ageing_swtmr_create(&g_lwip_bridge);
            if (ret == 0) {
                g_lwip_bridge.br_enable = TRUE;
                PRINTK("Bridge Enable!\n");
            }
            else {
                g_lwip_bridge.br_enable = FALSE;
                PRINT_ERR("Bridge enable failed!\n");
            }
        }
        else {
            PRINTK("Bridge has already been enabled!\n");
        }
    }
    else
    {
        if (g_lwip_bridge.br_enable == TRUE)
        {
            ret = bridge_ageing_swtmr_delete();
            if(ret != LOS_OK){
                PRINT_ERR("LOS_SwtmrStop error %d\n", ret);
                return;
            }

            db_cleanup(g_lwip_bridge.nethash);
            db_cleanup(g_lwip_bridge.porthash);
            g_lwip_bridge.br_enable = FALSE;
            PRINTK("Bridge Disable!\n");
        }
        else {
            PRINTK("Bridge has not been enabled!\n");
        }
    }
}

static err_t netif_link_input(struct netif *netif, struct pbuf *p)
{
  int ret = ERR_OK;

  /* full packet send to tcpip_thread to process */
  if (netif->input) {
    ret = netif->input(p, netif);
  }

  if (ret != ERR_OK) {
     LWIP_DEBUGF(DRIVERIF_DEBUG, ("driverif_input: IP input error\n"));
     (void)pbuf_free(p);
     LINK_STATS_INC(link.drop);
     LINK_STATS_INC(link.link_rx_drop);
     if (ret == ERR_MEM)
     {
          LINK_STATS_INC(link.link_rx_overrun);
     }
   } else {
     LINK_STATS_INC(link.recv);
   }

   return ret;
}

static err_t netif_link_output(struct netif *netif, struct pbuf *p)
{
#if ETH_PAD_SIZE
    (void)pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

    netif->drv_send(netif, p);

#if ETH_PAD_SIZE
    (void)pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.xmit);

    return ERR_OK;
}

static void proxy_arp(struct netif *netif, struct pbuf *p)
{
    struct pbuf *temp_p = NULL;
    struct eth_arphdr *arp = NULL;
    struct eth_hdr *ethhdr = NULL;
    int ret;

    ethhdr = (struct eth_hdr *)p->payload;
    arp = (struct eth_arphdr *)(((char *)p->payload) + sizeof(struct eth_hdr));

    temp_p = proxy_arp_rcv_req(&g_lwip_bridge, p, arp);
    if (temp_p != NULL) {
        (void)memcpy_s((char *)ethhdr->src.addr, sizeof(ethhdr->src.addr), netif->hwaddr, NETIF_MAX_HWADDR_LEN);
        ret = netif_link_output(netif, temp_p);
        if (ret != ERR_OK) {
            PRINT_ERR("ARP:ap0->ap0\n");
        }
        (void)pbuf_free(temp_p);
    }
}

static int arp_input_forward(struct netif *netif, struct pbuf *p)
{
    struct eth_arphdr *arp = NULL;
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
    unsigned int status = 0;
    struct netif *netif_o = NULL;
    struct eth_hdr *ethhdr = NULL;
    int ret;

    ethhdr = (struct eth_hdr *)p->payload;
    arp = (struct eth_arphdr *)(((char *)p->payload) + sizeof(struct eth_hdr));

    if (arp->us_ar_pro != htons(ETHER_TYPE_IP)) {
        PRINT_ERR("The ARP Packet is error!!!\n");
        return BRIDGE_ERROR;
    }

    /* save mac and port relation */
    db_port_insert(&g_lwip_bridge, ethhdr->src.addr, netif);

    if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
        if (!memcmp(g_lwip_bridge.br_ip, arp->auc_ar_tip, ETH_TARGET_IP_ADDR_LEN)) {
            bridge_print("ARP:dst input local...\n");
            return BRIDGE_LOCAL;
        }

        bridge_print("ARP:dst->src\n");
        if (arp->us_ar_op == htons(ARP_REQUEST)) {
            /* proxy arp and response */
            netif_o = g_lwip_bridge.dst;
            proxy_arp(netif_o, p);

            /* forward arp response */
            generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_sip);
            db_network_insert(&g_lwip_bridge, ethhdr->src.addr, networkAddr);
            if (ethhdr->dest.addr[0] != 0xff) {
                ethhdr->dest.addr[0] = 0xff;
                ethhdr->dest.addr[1] = 0xff;
                ethhdr->dest.addr[2] = 0xff;
                ethhdr->dest.addr[3] = 0xff;
                ethhdr->dest.addr[4] = 0xff;
                ethhdr->dest.addr[5] = 0xff;
            }
        } else if (arp->us_ar_op == htons(ARP_REPLY)) {
                generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_sip);
                db_network_insert(&g_lwip_bridge, ethhdr->src.addr, networkAddr);
                generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_tip);
                status = db_network_lookup_and_replace(&g_lwip_bridge, p, networkAddr);
                if (status == 0) {
                    PRINT_ERR("+++find db failed!++\n");
                }
                if (memcpy_s(arp->auc_ar_tha, sizeof(arp->auc_ar_tha),
                     ((char *)p->payload) + 2, NETIF_MAX_HWADDR_LEN) != 0) {
                  return BRIDGE_ERROR;
                }
        } else {
            ;
        }

        netif_o = g_lwip_bridge.src[0];
        ret = netif_link_output(netif_o, p);
        if (ret != ERR_OK) {
            PRINT_ERR("ARP:sta0->ap0\n");
        }

        return BRIDGE_FORWARD;
    } else {
        bridge_print("ARP:ap0->sta0\n");
        if (!memcmp(g_lwip_bridge.br_ip, arp->auc_ar_tip, ETH_TARGET_IP_ADDR_LEN)) {
            bridge_print("ARP:src input local...\n");
            netif_o = g_lwip_bridge.dst;
            ret = netif_link_input(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("UDP:str->dst\n");
            }
            return BRIDGE_FINISH;
        }

        netif_o = g_lwip_bridge.dst;
        if (arp->us_ar_op == htons(ARP_REQUEST)) {
            struct pbuf *temp_p = NULL;
            temp_p = proxy_arp_rcv_req(&g_lwip_bridge, p, arp);
            if (temp_p != NULL) {
                struct eth_hdr *ethhdr_t = (struct eth_hdr *)temp_p->payload;
                (void)memcpy_s((char *)ethhdr_t->src.addr, sizeof(ethhdr_t->src.addr), netif_o->hwaddr, NETIF_MAX_HWADDR_LEN);

                ret = netif_link_output(netif_o, temp_p);
                if (ret != ERR_OK) {
                    PRINT_ERR("ARP:ap0->ap0\n");
                }
                (void)pbuf_free(temp_p);
            }

            generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_sip);
            db_network_insert(&g_lwip_bridge, ethhdr->src.addr, networkAddr);
            if (memcpy_s(ethhdr->src.addr, sizeof(ethhdr->src.addr), g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN) != 0) {
              PRINT_ERR("arp_input_forward:memcpy_s fail!\n");
            }
            if (ethhdr->dest.addr[0] != 0xff) {
                ethhdr->dest.addr[0] = 0xff;
                ethhdr->dest.addr[1] = 0xff;
                ethhdr->dest.addr[2] = 0xff;
                ethhdr->dest.addr[3] = 0xff;
                ethhdr->dest.addr[4] = 0xff;
                ethhdr->dest.addr[5] = 0xff;
            }
        } else if (arp->us_ar_op == htons(ARP_REPLY)) {
            generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_sip);
            db_network_insert(&g_lwip_bridge, ethhdr->src.addr, networkAddr);
            generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp->auc_ar_tip);
            status = db_network_lookup_and_replace(&g_lwip_bridge, p, networkAddr);
            if (status == 0) {
                PRINT_ERR("----find db failed!----\n");
            }
            (void)memcpy_s(arp->auc_ar_tha, sizeof(arp->auc_ar_tha), ethhdr->dest.addr, NETIF_MAX_HWADDR_LEN);
        }

        ret = netif_link_output(netif_o, p);
        if (ret != ERR_OK) {
            PRINT_ERR("ARP:ap0->sta0\n");
        }

        return BRIDGE_FORWARD;
    }
}

static int ipv4_input_forward(struct netif *netif, struct pbuf *p) /*lint -e505*/
{
    struct ip_hdr *iph = NULL;
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
    struct netif *netif_o = NULL;
    struct eth_hdr *ethhdr = NULL;
    struct udp_hdr *udph = NULL;
    struct dhcp_msg *dhcpmsg = NULL;
    int ret;

    ethhdr = (struct eth_hdr *)p->payload;
    iph = (struct ip_hdr *)(((char *)p->payload) + sizeof(struct eth_hdr));

#if DEBUG
    if (iph->_proto == IPPROTO_TCP) {
    char *buf;
    buf = (char *)p->payload + 2 + 54;
    bridge_print("tcp[%02x %02x %02x %02x %02x %02x]\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
    }
    else if (iph->_proto == IPPROTO_UDP) {
        bridge_print("udp----\n");
    }
#endif

    if (iph->_proto == IPPROTO_UDP) {
        bridge_print("UDP:start\n");
        udph = (struct udp_hdr *)((unsigned long)iph + ((iph->_v_hl & 0xf0 >> 4) << 2));

        if ((udph->src == htons(SERVER_PORT)) && (udph->dest == htons(CLIENT_PORT)))  {// DHCP request
            if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
                bridge_print("dst->src not support forward DHCP Request!\n");
                return BRIDGE_ERROR;
            }

            dhcpmsg = (struct dhcp_msg *)((uintptr_t)udph + sizeof(struct udp_hdr));
            if(dhcpmsg->cookie == htonl(DHCP_MAGIC)) {
                bridge_print("UDP:src->dst\n");
                netif_o = g_lwip_bridge.dst;
                dhcpmsg->flags = htons(0xffff);//0x8000

                ret = netif_link_output(netif_o, p);
                if (ret != ERR_OK) {
                    PRINT_ERR("UDP:str->dst\n");
                }

                return BRIDGE_FORWARD;
            }
        } else if ((udph->src == htons(CLIENT_PORT)) && (udph->dest == htons(SERVER_PORT))) {// DHCP reply
            dhcpmsg= (struct dhcp_msg *)((uintptr_t)udph + sizeof(struct udp_hdr));

            if(dhcpmsg->cookie == htonl(DHCP_MAGIC)) {
                if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
                    if (memcmp(dhcpmsg->chaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
                        bridge_print("UDP:dst->src\n");

                        netif_o = g_lwip_bridge.src[0];
                        ret = netif_link_output(netif_o, p);
                        if (ret != ERR_OK) {
                            PRINT_ERR("UDP:dst->src\n");
                        }

                        return BRIDGE_FORWARD;
                    } else {
                        (void)memcpy_s(g_lwip_bridge.br_ip, sizeof(g_lwip_bridge.br_ip), &dhcpmsg->yiaddr, sizeof(ip_addr_t));

                        bridge_print(">br_mac[%02x %02x %02x %02x %02x %02x], br_ip[%d %d %d %d]<\n",
                        g_lwip_bridge.br_mac[0], g_lwip_bridge.br_mac[1], g_lwip_bridge.br_mac[2],
                        g_lwip_bridge.br_mac[3], g_lwip_bridge.br_mac[4], g_lwip_bridge.br_mac[5],
                        g_lwip_bridge.br_ip[0], g_lwip_bridge.br_ip[1], g_lwip_bridge.br_ip[2],
                        g_lwip_bridge.br_ip[3]);

                        bridge_print("UDP:dst->local\n");

                        return BRIDGE_LOCAL;
                    }
                }
            }
        } else {
            bridge_print("UDP:port(%d %d)\n", ntohs(udph->src), ntohs(udph->dest)); /*lint !e534*/
        }
    }

#if DEBUG
    bridge_print("icmp_mac[%02x %02x %02x %02x %02x %02x], icmp_ip[%d %d %d %d]<\n",
       netif->hwaddr[0], netif->hwaddr[1], netif->hwaddr[2],
       netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5],
      *((unsigned char *)&iph->dest.addr), *(((unsigned char *)&iph->dest.addr)+1), *(((unsigned char *)&iph->dest.addr)+2),
       *(((unsigned char *)&iph->dest.addr)+3));
#endif

    if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
        /* broadcast and multicast */
        if (ip_addr_ismulticast(&iph->dest) || ip_addr_isbroadcast(&iph->dest, netif)) {
            netif_o = g_lwip_bridge.src[0];
            ret = netif_link_output(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }

            return BRIDGE_LOCAL;
        }

        /* unicast */
        generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, (unsigned char *)&iph->dest.addr);
        if (db_network_lookup_and_replace(&g_lwip_bridge, p, networkAddr)) {
            netif_o = g_lwip_bridge.src[0];
            ret = netif_link_output(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }

            return BRIDGE_FORWARD;
        }

        return BRIDGE_LOCAL;
    } else {
        bridge_print("ICMP:src->dst\n");
        if (!memcmp(g_lwip_bridge.br_ip, (unsigned char *)&iph->dest.addr, ETH_TARGET_IP_ADDR_LEN)) {
            bridge_print("ICMP:src input local...\n");
            netif_o = g_lwip_bridge.dst;
            ret = netif_link_input(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("UDP:str->dst\n");
            }
            return BRIDGE_FINISH;
        }

        generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, (unsigned char *)&iph->src.addr);
        db_network_insert(&g_lwip_bridge, ethhdr->src.addr, networkAddr);

        netif_o = g_lwip_bridge.dst;
        if (memcpy_s(ethhdr->src.addr, sizeof(ethhdr->src.addr), g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN) != 0) {
          PRINT_ERR("ipv4_input_forward:memcpy_s fail!\n");
        }

        ret = netif_link_output(netif_o, p);
        if (ret != ERR_OK) {
            PRINT_ERR("ICMP:src->dst\n");
        }

        /* broadcast and multicast */
        if (ip_addr_ismulticast(&iph->dest) || ip_addr_isbroadcast(&iph->dest, netif)) {
            netif_o = g_lwip_bridge.dst;
            ret = netif_link_input(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }

            return BRIDGE_FINISH;
        }

        return BRIDGE_FORWARD;
    }
}/*lint +e505*/


static int arp_output_forward(struct netif *netif, struct pbuf *p) /*lint -e505*/
{
    struct eth_arphdr *arp = NULL;
    struct netif *netif_o = NULL;
    struct eth_hdr *ethhdr = NULL;
    u8_t  broadcast[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};/*lint !e78*/
    int ret;

    ethhdr = (struct eth_hdr *)p->payload;
    arp = (struct eth_arphdr *)(((char *)p->payload) + sizeof(struct eth_hdr));

    if (arp->us_ar_pro != htons(ETHER_TYPE_IP)) {
        PRINT_ERR("The ARP Packet is error!!!\n");
        return BRIDGE_ERROR;
    }

    if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
        bridge_print("arp_output_forward:line:%d\n",  __LINE__);
        if (0 == memcmp(ethhdr->dest.addr, broadcast, ETHER_ADDR_LEN)) {
            bridge_print("arp_output_forward:line:%d\n",  __LINE__);
            netif_o = g_lwip_bridge.src[0];
            ret = netif_link_output(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }
        } else {
            /* unicast */
            if ((NULL != (netif_o = db_port_lookup(&g_lwip_bridge, ethhdr->dest.addr))) && (netif != netif_o)) {
                bridge_print("arp_output_forward:line:%d\n",  __LINE__);
                ret = netif_link_output(netif_o, p);
                if (ret != ERR_OK) {
                    PRINT_ERR("ICMP:dst->src\n");
                }

                return BRIDGE_FORWARD;
            }
        }
    }

    return BRIDGE_LOCAL;
}/*lint +e505*/

static int ipv4_output_forward(struct netif *netif, struct pbuf *p) /*lint -e505*/
{
    struct ip_hdr *iph;
    struct netif *netif_o;
    struct eth_hdr *ethhdr;
    int ret;

    ethhdr = (struct eth_hdr *)p->payload;
    iph = (struct ip_hdr *)(((char *)p->payload) + sizeof(struct eth_hdr));

    if (!memcmp(netif->hwaddr, g_lwip_bridge.br_mac, NETIF_MAX_HWADDR_LEN)) {
        bridge_print("ipv4_output_forward:line:%d\n",  __LINE__);
        /* broadcast and multicast */
        if (ip_addr_ismulticast(&iph->dest) || ip_addr_isbroadcast(&iph->dest, netif)) {
            netif_o = g_lwip_bridge.src[0];
            ret = netif_link_output(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }

            return BRIDGE_LOCAL;
        }

        /* unicast */
        if ((NULL != (netif_o = db_port_lookup(&g_lwip_bridge, ethhdr->dest.addr))) && (netif != netif_o)) {
            bridge_print("ipv4_output_forward:line:%d\n",  __LINE__);
            ret = netif_link_output(netif_o, p);
            if (ret != ERR_OK) {
                PRINT_ERR("ICMP:dst->src\n");
            }

            return BRIDGE_FORWARD;
        }
    }

    return BRIDGE_LOCAL;
}/*lint +e505*/

int bridge_input(struct netif *netif, struct pbuf *p)
{
    u16_t ethhdr_type;
    struct eth_hdr *ethhdr;
    err_t ret = BRIDGE_ERROR;

    if (FALSE == g_lwip_bridge.br_enable) {
        return BRIDGE_LOCAL;
    }

    ethhdr = (struct eth_hdr *)p->payload;
    ethhdr_type = ntohs(ethhdr->type);

    switch (ethhdr_type) {
    case ETHER_TYPE_ARP:
        ret = arp_input_forward(netif, p);
        break;
    case ETHER_TYPE_IP:
     ret  = ipv4_input_forward(netif, p);
     break;
    default:
        break;
    }

    return ret;
}

int bridge_output(struct netif *netif, struct pbuf *p)
{
    u16_t ethhdr_type;
    struct eth_hdr *ethhdr = NULL;
    err_t ret = BRIDGE_ERROR;

    if (FALSE == g_lwip_bridge.br_enable) {
        return BRIDGE_LOCAL;
    }

    ethhdr = (struct eth_hdr *)p->payload;
    ethhdr_type = ntohs(ethhdr->type);

    switch (ethhdr_type) {
    case ETHER_TYPE_ARP:
        ret = arp_output_forward(netif, p);
        break;
    case ETHER_TYPE_IP:
     ret  = ipv4_output_forward(netif, p);
     break;
    default:
        break;
    }

    return ret;
}

#endif

